<?php

/*
 * 任务计划
 * @author birdy <freebirdy@foxmail.com>
 * @link http://www.aiyuangong.com
 */

namespace app\admin\command\cron;

use app\admin\command\Base;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Db;

class Cron extends Base
{
    //任务类型
    protected $type;
    protected $excuteids = [];
    protected $tips = 'datas';
    protected $options = [];
    protected $classname;

    //处理任务计划中的待执行任务
    public function execute(Input $input, Output $output)
    {
        $this->classname = get_class($this);
        if ($this->classname != __CLASS__) {
            return $this->executeTasks($input, $output);
        }
        
        $map = [];
        $map['real_exetime'] = 0;
        if ($input->hasOption('type')) {
            $map['type'] = $input->getOption('type');
        }

        $limit = 9999;
        if ($input->hasOption('limit')) {
            $limit = intval($input->getOption('limit'));
            $limit = $limit > 0 ? $limit : 9999;
        }

        $cron = $this->getExecuteTasks($input);
        if (!$cron) {
            return;
        }

        $this->_showLog('tasks execute start', 'step');

        //对任务进行分组，加快执行效率
        $cron = group_arr_key($cron, 'type');
        foreach ($cron as $group => $cron) {
            if (is_numeric($group)) {
                $groupMap = [
                    1 => 'onsale', //商品上架
                    2 => 'update_promote_price', //更新商品促销价格
                    3 => 'bl_goods_expired', //优惠套装过期
                    4 => 'recommend_expired', //推荐展位过期
                    5 => 'groupon_price', //团购开始更新商品促销价格
                    6 => 'groupon_expired', //团购过期
                    7 => 'discount_expired' //限时折扣过期
                ];
                $group = isset($groupMap[$group]) ? $groupMap[$group] : '';
            }

            if ($group) {
                \think\Console::call('cron:' . $group, ['--data', $cron], 'console');
            } else {
                $this->_showLog('unknow task:' . $group);
            }
        }

        $this->_showLog('tasks execute end', 'step');
    }

    //执行任务
    private function executeTasks(Input $input, Output $output, callable $callable = null)
    {
        $data = $this->getExecuteTasks($input);
        if (!$data) {
            return;
        }

        $total = count($data);

        if (!is_callable($callable)) {
            $callable = [$this, 'executeLogic'];
        }

        $this->_showLog($this->tips . '：(0/' . $total . ")", 'step');

        call_user_func($callable, $data);

        if ($this->excuteids) {
            $this->updateExecuteTime();
            $this->_showLog($this->tips . '：(' . $total . '/' . $total . ")", 'step');
        }
    }

    //数据处理实据逻辑
    protected function executeLogic($data)
    {
        $this->_showLog($this->tips . '：You need to override the method "executeLogic" in class "'.$this->classname.'" to deal datas', 'step');
    }

    //获取要处理的数据
    protected function getExecuteTasks(Input $input)
    {
        $cron = [];
        $map = [];
        if ($input->hasOption('data')) {
            $data = $input->getOption('data');
            if (is_array($data)) {
                $cron = $data;
            } else {
                $data = array_filter(explode(',', $data));
                $map['id'] = ['in', $data];
            }
        }

        if (!$cron) {
            $map['real_exetime'] = 0;
            $map['exetime'] = ['elt', time()];
            if ($this->type) {
                $map['type'] = ['in', $this->type];
            } else if ($input->hasOption('type')) {
                $map['type'] = ['in', $input->getOption('type')];
            }

            $limit = 9999;
            if ($input->hasOption('limit')) {
                $limit = intval($input->getOption('limit'));
                $limit = $limit > 0 ? $limit : 9999;
            }

            $cron = Db::name('cron')->where($map)->limit($limit)->select();
            if (!$cron) {
                $this->_showLog('no tasks need to execute', 'step');
            }
        }

        return $cron ?: [];
    }

    //更新任务实际执行时间
    protected function updateExecuteTime($ids = [])
    {
        if (!$ids) {
            $ids = $this->excuteids;
            $this->excuteids = [];
        }

        if (!$ids) {
            return;
        }

        $update = $update ?: [];
        $update['real_exetime'] = time();

        $map = [
            'real_exetime' => 0,
            'id' => ['in', $ids]
        ];

        return Db::name('cron')->where($map)->update($update);
    }

    //命令行配置说明
    protected function configure()
    {
        $name = get_class($this);
        if ($name == __CLASS__) {
            $this->setName('cron')
                    ->addOption('type', 't', Option::VALUE_OPTIONAL, 'type to exec', null)
                    ->addOption('limit', 'l', Option::VALUE_OPTIONAL, 'exec nums each time', 0)
                    ->setDescription('处理商城的异步消息队列');
        } else {
            $this->configure_task();
        }
    }

    //任务命令行配置说明
    protected function configure_task()
    {
        if (!is_array($this->type)) {
            $this->type = explode(',');
        }
        $type = $this->type[0];

        $this->setName('cron:' . $type)
                ->addOption('data', 'd', Option::VALUE_OPTIONAL, 'data to exec', null)
                ->addOption('limit', 'l', Option::VALUE_OPTIONAL, 'exec nums each time', 0)
                ->setDescription($this->tips);

        if ($this->options) {
            foreach ($this->options as $option) {
                $option[1] = isset($option[1]) ? $option[1] : null;
                $option[2] = isset($option[2]) ? $option[2] : null;
                $option[3] = isset($option[3]) ? $option[3] : '';
                $option[4] = isset($option[4]) ? $option[4] : null;
                $this->addOption($option[0], $option[1], $option[2], $option[3], $option[4]);
            }
        }
    }

}
